#!/usr/bin/env python  
# -*- coding: utf-8 -*-  

#
#
#

import errno  
import fuse  
import stat  
import time  
import os, math, sqlite3, hashlib, zlib, cPickle, sys, struct, zlib
  
fuse.fuse_python_api = (0, 2)  

class eopen(file):
    def Rread(self, size=-1):
        self.seek(-size, 1)
        data = self.read(size)[::-1]
        self.seek(-size, 1)
        return data


    def Rwrite(self, data):
        c = self.tell()
        self.seek(-len(data), 1)
        n = self.tell()
        if c - n == len(data):
            self.write(data[::-1])
            self.seek(-len(data), 1)
        else:
            return False

class MyFS(fuse.Fuse):  
    def __init__(self, *args, **kw):  
        fuse.Fuse.__init__(self, *args, **kw)  
        self.Block = "/dev/sdb1"
        bd = open(self.Block, "r")
        t = bd.read(5)
        if t != "dupFS":
            print "Error, not right file type"
            sys.exit(1)
        self.header = cPickle.load(bd)
        bd.seek(self.header['metaStart'])
        self.meta = cPickle.load(bd)
        self.LastFile=False
        self.LastHash=False
        bd.close

    def getPath(self, path):
        if path == "/":
            return self.meta['path']
        s = path.split("/")
        s.pop(0)
        base = s.pop()
        t = self.meta['path']['sub']
        for i in s:
            if i in t:
                t = t[i]['sub']
            else:
                return -errno.ENOENT
        if base in t:
            t = t[base]
        else:
            return -errno.ENOENT
        return t

    def mknod(self, path, mode, dev):
        if path == "/.syncALL":
            self.fsync("","")
            return 0
        s = path.split("/")
        new = s.pop()
#        if "".join(s) != "":
        t = self.getPath(os.path.dirname(path))
#        else:
#            t = self.meta['path']
#        t = self.meta['path']['sub']
#        for i in s:
        t = t['sub']
        tmp = self.GetContext()
        t[new]=dict()
        t[new]['type']=stat.S_IFREG
        t[new]['mode']=mode
        t[new]['ctime']=int(time.time())
        t[new]['atime']=t[new]['ctime']
        t[new]['mtime']=t[new]['ctime']
        t[new]['uid']=tmp['uid']
        t[new]['gid']=tmp['gid']
        t[new]['size']=0
        t[new]['hash']=""
        return 0

    def mkdir(self, path, mode):
        print "MKDIR"
        s = path.split("/")
        s.pop(0)
        new = s.pop()
        t = self.meta['path']['sub']
        for i in s:
            t = t[i]['sub']
        tmp = self.GetContext()
        t[new]=dict()
        t[new]['type']=stat.S_IFDIR
        t[new]['mode']=mode
        t[new]['ctime']=int(time.time())
        t[new]['atime']=t[new]['ctime']
        t[new]['mtime']=t[new]['ctime']
        t[new]['size']=0
        t[new]['uid']=tmp['uid']
        t[new]['gid']=tmp['gid']
        t[new]['sub']=dict()
        

    def readdir(self, path, offset):  
        print "READDIR %s"%path
        send=['.','..']
        s = path.split("/")
        s.pop(0)
        if path == "/":
            s = list()
        t = self.meta['path']['sub']
        for i in s:
            t = t[i]['sub']
        for k,v in t.items():
            send.append(k)
        for x in send:
            yield fuse.Direntry(x)


    def getattr(self, path):  
        print "GETATTR %s"%path
        s = path.split("/")
        s.pop(0)
        base = s.pop()
        t = self.meta['path']['sub']
        for i in s:
            if i in t:
                t = t[i]['sub']
            else:
                return -errno.ENOENT
        if path == "/":
            t = self.meta['path']
        else:
            if base in t:
                t = t[base]
            else:
                return -errno.ENOENT
        st=fuse.Stat()
        st.st_mode = t['type'] | t['mode']
        st.st_nlink = 1
        st.st_atime = t['atime']
        st.st_mtime = t['mtime']
        st.st_ctime = t['ctime']
        st.st_uid = t['uid']
        st.st_gid = t['gid']
        st.st_size = t['size']
        return st

    def write(self, path, data, offset):
#        print "WRITE ", path, len(data)
        free = self.header['metaStart']-self.meta['lastPos']
        if free < len(data):
            return -errno.ENOSPC
        s = path.split("/")
        s.pop(0)
        f = s.pop()
        t = self.meta['path']['sub']
        for i in s:
            if i in t:
                t = t[i]['sub']
            else:
                return -errno.ENOENT
        if t[f]['type'] != stat.S_IFREG:
            return -errno.EPERM
        if t[f]['hash'] == "" and offset == 0:
            print "Free space: ", free
            print "Used: ", self.header['totalSize']-free
#            self.fsync("","")
            self.LastFile=path
            self.LastHash=hashlib.sha1()
            self.LastHash.update(data)
            sha = self.LastHash.hexdigest()
            if sha in self.meta['hashes']:
                t[f]['hash'] = sha
                t[f]['size'] = len(data)
                self.meta['hashes'][sha]['count']+=1
            else:
                t[f]['hash'] = sha
                self.meta['hashes'][sha]=dict()
                self.meta['hashes'][sha]['pos']=self.meta['lastPos']
                self.meta['hashes'][sha]['count']=1
                t[f]['size'] = len(data)
                bd = open(self.Block, "rb+")
                bd.seek(self.meta['lastPos'])
                bd.write(data)
                self.meta['lastPos']=bd.tell()
                bd.close()
            return len(data)
        elif t[f]['hash'] != "" and self.LastFile==path:
            bd = open(self.Block, "rb+")
            bd.seek(self.meta['hashes'][t[f]['hash']]['pos'])
            if offset != t[f]['size']:
                return -errno.EPERM
            self.LastHash.update(data)
            fullSha = self.LastHash.hexdigest()
            if fullSha in self.meta['hashes']:
                self.meta['hashes'][t[f]['hash']]['count'] -= 1
                if self.meta['hashes'][t[f]['hash']]['count']==0:
                    self.meta['lastPos']=self.meta['hashes'][t[f]['hash']]['pos']
                    del(self.meta['hashes'][t[f]['hash']])
                t[f]['hash']=fullSha
                t[f]['size']+=len(data)
                self.meta['hashes'][fullSha]['count']+=1
            else:
                self.meta['hashes'][fullSha]=dict()
                self.meta['hashes'][fullSha]['count']=1
		if self.meta['lastPos'] == self.meta['hashes'][t[f]['hash']]['pos']+t[f]['size']:
                    self.meta['hashes'][fullSha]['pos']=self.meta['hashes'][t[f]['hash']]['pos']
                else:
                    self.meta['hashes'][fullSha]['pos']=self.meta['lastPos']
                    wrote = 0
                    left = t[f]['size'] 
                    while wrote < t[f]['size']:
                        bd.seek(self.meta['hashes'][t[f]['hash']]['pos']+wrote)
                        if left > 4096:
                            tmp=bd.read(4096)
                            left-=4096
                        else:
                            tmp=bd.read(left)
                            left-=left
                        bd.seek(self.meta['hashes'][fullSha]['pos']+wrote)
                        wrote+=len(tmp)
                self.meta['hashes'][t[f]['hash']]['count']-=1
                if self.meta['hashes'][t[f]['hash']]['count'] == 0:
                    del(self.meta['hashes'][t[f]['hash']])
                t[f]['hash'] = fullSha
                bd.seek(self.meta['hashes'][fullSha]['pos']+offset, 0)
                bd.write(data)
                self.meta['lastPos'] = bd.tell()
                t[f]['size']+=len(data)
            bd.close()
            return len(data)             
        return -errno.EPERM

    def chmod(self, path, mode):
        s = path.split("/")
        s.pop(0)
        base = s.pop()
        t = self.meta['path']['sub']
        for i in s:
            if i in t:
                t = t[i]['sub']
            else:
                return -errno.ENOENT
        if path == "/":
            t = self.meta['path']
        else:
            if base in t:
                t = t[base]
            else:
                return -errno.ENOENT
        t['mode']=mode


    def chown(self, path, uid, gid):
        s = path.split("/")
        s.pop(0)
        base = s.pop()
        t = self.meta['path']['sub']
        for i in s:
            if i in t:
                t = t[i]['sub']
            else:
                return -errno.ENOENT
        if path == "/":
            t = self.meta['path']
        else:
            if base in t:
                t = t[base]
            else:
                return -errno.ENOENT
        t['gid']=gid
        t['uid']=uid


    def fsync(self, path, isFsyncFile):
        print "GOT SYNC"
        bd = open(self.Block, "rb+")
        tmp = cPickle.dumps(self.meta,2)
        start = self.header['totalSize']-len(tmp)
        print start, self.meta['lastPos']
        if start > self.meta['lastPos']:
            print "reWriting"
            bd.seek(start)
            bd.write(tmp)
            bd.seek(5)
            self.header['metaStart']=start
            bd.write(cPickle.dumps(self.header, 2))
        bd.close()

    def rename(self,oldPath,newPath ):
	print "RENAME"
        t = self.getPath(oldPath)
        if t['type'] == stat.S_IFDIR:
            self.mkdir(newPath, t['mode'])
        else:
            self.mknod(newPath, t['mode'], 0)
        n = self.getPath(newPath)
        n['ctime'] = t['ctime']=int(time.time())
        n['atime'] = t['atime']
        n['mtime'] = t['mtime']
        n['size'] = t['size']
        n['uid'] = t['uid']
        n['gid'] = t['gid']
        if t['type'] == stat.S_IFDIR:
            n['sub'] = t['sub']
        else:
            n['hash'] = t['hash']
        s = self.getPath(os.path.dirname(oldPath))
        del(s['sub'][os.path.basename(oldPath)])
        return 0



    def rmdir(self, path):
        t = self.getPath(path)
        if len(t['sub']) > 0:
            return -errno.EPERM
        s = self.getPath(os.path.dirname(path))
        del(s['sub'][os.path.basename(path)])

    def unlink(self, path):
        t = self.getPath(path)
        if t['size'] > 0 and self.LastFile!=path:
            return -errno.EPERM
        elif self.LastFile == path and t['size'] > 0:
            self.meta['lastPos']==self.meta['hashes'][t['hash']]['pos']
        s = self.getPath(os.path.dirname(path))
        del(s['sub'][os.path.basename(path)])

        
    def open(self, path, flags):
        return 0

    def utimens(self, path, atime, mtime):
        s = path.split("/")
        s.pop(0)
        base = s.pop()
        t = self.meta['path']['sub']
        for i in s:
            if i in t:
                t = t[i]['sub']
            else:
                return -errno.ENOENT
        if path == "/":
            t = self.meta['path']
        else:
            if base in t:
                t = t[base]
            else:
                return -errno.ENOENT
        t['atime']=atime.tv_sec
        t['mtime']=mtime.tv_sec

        return 0


    def read(self, path, size, offset):
        s = path.split("/")
        s.pop(0)
        f = s.pop()
        t = self.meta['path']['sub']
        for i in s:
            if i in t:
                t = t[i]['sub']
            else:
                return -errno.ENOENT
        if t[f]['type'] != stat.S_IFREG:
            return -errno.EPERM
        if t[f]['hash'] in self.meta['hashes']:
            startPOS = self.meta['hashes'][t[f]['hash']]['pos']
            bd = open(self.Block, "rb")
            bd.seek(startPOS+offset)
            if size+offset > t[f]['size']:
                ret = bd.read(t[f]['size']-offset)
            else:
                ret = bd.read(size)
            bd.close()
            return ret
        elif t[f]['hash'] == "" and t[f]['size']==0:
            return 0
        return -errno.EPERM

    def truncate(self, path, length):
        return 0
        
        
        
        
        
        


        
        
      
if __name__ == '__main__':  
    fs = MyFS()  
    fs.parse(errex=1)  
    fs.main()  
